/*
 * @(#)LoginFilter.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 *
 */
package com.dcivision.framework.web;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jcifs.Config;
import jcifs.http.NtlmHttpFilter;
import jcifs.ntlmssp.Type1Message;
import jcifs.ntlmssp.Type2Message;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.util.Base64;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.Utility;
import com.dcivision.user.auth.AuthenticationHandler;
import com.dcivision.user.auth.BasicAuthenticationHandler;

/**
 * LoginFilter.java
 *
 * ServletFilter which allows paraDM to force user authentication. Set this Filter for all paths that you want to
 * protect.
 *
 * @author Charlie Liu
 * @company DCIVision Limited
 * @creation date 6/06/2005
 * @version $Revision: 1.3.2.9 $
 *
 */
public class LoginFilter extends NtlmHttpFilter {

  private Log logger = null;

  private boolean initFlag = false;

  private javax.servlet.FilterConfig filterConfig = null;

  /**
   * Constructor Creates a new instance of SecurityFilter
   */
  public LoginFilter() {
    super();
    logger = LogFactory.getLog(this.getClass().getName());
  }

  /**
   * Initialization, grab and use configuration parameters.
   */
  public void init(javax.servlet.FilterConfig filterConfig) throws javax.servlet.ServletException {
    this.filterConfig = filterConfig;
  }

  /**
   * Cleanup any system resources, etc....
   */
  public void destroy() {
    logger.info("LoginFilter: shutting down.");
    String authMethod = SystemParameterFactory.getSystemParameter(SystemParameterConstant.LOGIN_METHOD);
    // only call super.destroy() if the system is using the NTLM authentication method.
    if (authMethod.equalsIgnoreCase("NTLM")) {
      super.destroy();
    }
  }

  protected synchronized void lazyInit() throws javax.servlet.ServletException {
    if (!initFlag) {
      String authMethod = SystemParameterFactory.getSystemParameter(SystemParameterConstant.LOGIN_METHOD);
      if (GlobalConstant.LOGIN_METHOD_NTLM.equalsIgnoreCase(authMethod)) {
        String domainControllerIP = SystemParameterFactory
            .getSystemParameter(SystemParameterConstant.AUTHENTICATION_SSO_DOMAIN_CONTROLLER_IP);
        if (!Utility.isEmpty(domainControllerIP)) {
          Config.setProperty("jcifs.http.domainController", domainControllerIP);
        }
        super.init(filterConfig);
      } else {
        Config.setProperty("authMethod", "FORM");
      }
    }
    initFlag = true;
  }

  /**
   * Implement filtering. This is accomplished by using the configured ParaDM Authenticator to establish authenticity,
   * then either sending login challenge, or wrapping the request in a SecuredRequest (to allow access to authentication
   * information) and forwarding through the filter chain.
   */
  public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.FilterChain filterChain)
      throws java.io.IOException, javax.servlet.ServletException {

    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    if (!initFlag) {
      logger.debug("Lazy init Login filter");
      lazyInit();
    }

    //special handling to trick IE to skip re-authentication
    if (request instanceof HttpServletRequest) {
      HttpServletRequest httpRequest = (HttpServletRequest) request;
      if (httpRequest.getMethod().equals("POST")) {
        String msg = httpRequest.getHeader("Authorization");
        if ((msg != null) && (msg.startsWith("NTLM "))) {
          /* decode the NTLM response from the client */
          byte[] src = Base64.decode(msg.substring(5));

          //logger.debug("##################################");
          //logger.debug(httpRequest.getRequestURI());
          //logger.debug("Type:"+src[8]);
          //logger.debug("##################################");

          /* see if a type 1 message was sent by the client */
          if (src[8] == 1) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;

            Type1Message type1 = new Type1Message(src);

            /* respond with a type 2 message */
            Type2Message type2 = new Type2Message(type1, new byte[8], null);
            msg = Base64.encode(type2.toByteArray());
            httpResponse.setHeader("WWW-Authenticate", "NTLM " + msg);

            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.setContentLength(0);
            httpResponse.flushBuffer();
            return;
          }
        }
      }
    }

    try {
      if (request instanceof HttpServletRequest) {
        if (isLogin((HttpServletRequest) request, (HttpServletResponse) response)) {
          filterChain.doFilter(request, response);
        } else {
          logger.debug("Null request returned from processing.");
        }
      } else {
        logger.debug("Login ServletFilter, non-HttpServletRequest filter.");
        filterChain.doFilter(request, response);
      }
    } catch (ServletException sx) {
      logger.error("Error in filter chain.", sx);
      throw sx;
    } catch (java.io.IOException ix) {
      logger.error("Error in filter chain.", ix);
      throw ix;
    }
  }

  protected boolean isLogin(HttpServletRequest request, HttpServletResponse resp) throws java.io.IOException,
      javax.servlet.ServletException {

    String authMethod = SystemParameterFactory.getSystemParameter(SystemParameterConstant.LOGIN_METHOD);
    String uri = request.getRequestURI();

    if( null!= ((SessionContainer)request.getSession().getAttribute(GlobalConstant.SESSION_CONTAINER_KEY)) &&
        null!= ((SessionContainer)request.getSession().getAttribute(GlobalConstant.SESSION_CONTAINER_KEY)).getUserRecord()) {
      return (true);

    } else if(uri.lastIndexOf("LoginAs.do") >= 0
           || uri.indexOf("ScanDmsDocument.do") >= 0
           || uri.indexOf("ODMALogin.do") >= 0
           || uri.indexOf("Index.do") >= 0
           || com.dcivision.customize.CustomizedURIValidator.checkURI(uri)) {
      return (true);

    } else if ("BASIC".equalsIgnoreCase(authMethod)
            || request.getServletPath().indexOf("webdav") > 0) {
      logger.debug("BASIC LOGIN!");
      return new BasicAuthenticationHandler().authenticate(request, resp);

    } else if (GlobalConstant.LOGIN_METHOD_NTLM.equalsIgnoreCase(authMethod)) {
      boolean ntlmLoginSuccessFlag = false;
      logger.debug("NTLM LOGIN!");
      NtlmPasswordAuthentication ntlm = super.negotiate(request, resp, false);
      request.setAttribute("NTLM", ntlm);
      if (ntlm != null) {
        String loginName = ntlm.toString();
        loginName = loginName.substring(loginName.indexOf("\\") + 1);
        logger.debug("NTLM login:[" + ntlm + "], ParaDM Login:[" + loginName + "]");
        if (request.getSession().getAttribute(GlobalConstant.SESSION_CONTAINER_KEY) == null) {
          java.sql.Connection conn = null;
          AuthenticationHandler authHandler = null;
          try {
            conn = com.dcivision.framework.DataSourceFactory.getConnection();
            try {
              authHandler = (AuthenticationHandler) Class.forName(
                  SystemParameterFactory.getSystemParameter(SystemParameterConstant.AUTHENICATION_CLASS)).newInstance();
            } catch (Exception err) {
              logger.error("Authenication Handler class not found.", err);
              throw new ApplicationException(ErrorConstant.LOGIN_AUTH_CLASS_NOT_FOUND);
            }
            authHandler.initializeLoginUser(loginName, request, conn);
            conn.commit();
          } catch (Exception e) {
            try {
              conn.rollback();
            } catch (java.sql.SQLException sqlerr) {
            }
          } finally {
            try {
              conn.close();
            } catch (java.sql.SQLException sqlerr) {
            }
          }
        }
        ntlmLoginSuccessFlag = true;
      } else {
        ntlmLoginSuccessFlag = false;
      }
      return ntlmLoginSuccessFlag;
    }
    return true;
  }
}